;;; Example adapted from:
;;; https://github.com/spk121/guile-gi/files/7915746/gtk-tree-store.scm.txt

;;; Some formatting by me. Most comments by me.

(import (gi)
        (gi repository)
        (gi types)
        (gi util)
        ;; receive multiple values
        (ice-9 receive))

;; guile-gi internally creates GOOPS classes for GTK and other GObject
;; Introspection things. Methods of such classes can have the same name. The
;; question is how those methods are named, when importing methods of same name
;; into the same module. The following like tells Guile how to handle that
;; case. For more detail see:
;; https://www.gnu.org/software/guile/manual/html_node/Merging-Generics.html
(push-duplicate-handler! 'merge-generics)


(use-typelibs ("GLib" "2.0")
              ;; Which thing is renamed to what?
              (("Gio" "2.0") #:renamer (protect* '(application:new receive)))
              ;; Which thing is renamed to what?
              (("Gtk" "3.0") #:renamer (protect* '(tree-store:new) 'gtk::))
              ("Gdk" "3.0"))


(define (activate app)
  (let* (#|main window|#
         [window (application-window:new app)]
         ;; layout manager: grid layout
         [grid (grid:new)]
         [store
          ;; Create a store, which accepts the types, which
          ;; the columns will contain. The store will back
          ;; the tree view and contain the actual data. The
          ;; data will be rendered by renderers, which one
          ;; needs to specify. The rendered data will be
          ;; displayed in the tree view.
          (gtk::tree-store:new (vector G_TYPE_INT G_TYPE_STRING))]
         [treeview (tree-view:new-with-model store)]
         ;; Columns and renderers. Renderers are default
         ;; text renderers.
         [column1 (tree-view-column:new)]
         [renderer1 (cell-renderer-text:new)]
         [column2 (tree-view-column:new)]
         [renderer2 (cell-renderer-text:new)])
    ;; Connect the window delete event with a closure, which
    ;; destroys the window and quits the main loop.
    (connect window
             delete-event
             ;; The callback gets 2 arguments. The widget
             ;; from which the event originated and the
             ;; event itself.
             (λ (window event)
               (gtk-widget-destroy window)
               (gtk-main-quit)
               #f)) ;; do not stop the event propagation

    ;; The tree view has columns and those columns have
    ;; titles. For those we set a title.
    (set-title column1 "Column 1")
    ;; (pack the column) Tell the tree view, that to make
    ;; the columns change their width to snugly fit their
    ;; content. For that, the tree view needs to know, how
    ;; to render its contents. This is why a renderer needs
    ;; to be specified.
    (pack-start column1 renderer1 #f)
    ;; ???
    (add-attribute column1 renderer1 "text" 0)
    ;; Add the column to the tree view.
    (append-column treeview column1)
    ;; Set the title for the other column.
    (set-title column2 "Column 2")
    ;; (pack the column)
    (pack-start column2 renderer2 #f)
    ;; ???
    (add-attribute column2 renderer2 "text" 1)
    ;; Add the column to the tree view.
    (append-column treeview column2)

    (let ([iter (make <GtkTreeIter>)]
          [val1 (make <GValue>)]
          [val2 (make <GValue>)])
      ;; Set an integer value to val1.
      (set! (val1 G_TYPE_INT) 0)
      ;; Set an integer value to val2.
      (set! (val2 G_TYPE_STRING) "hello world")
      ;; Insert the values into the store, which is backing
      ;; the tree view.
      (tree-store:insert-with-values! store
                                      iter
                                      #f
                                      0
                                      (list->int-vector '(0 1))
                                      (vector val1 val2)))
    ;; Compose widgets.
    (add window grid)
    (add grid treeview)
    ;; Display the whole thing.
    (show-all window)))


(define (main)
  (let ((app (application:new "org.gtk.example" (number->application-flags 0))))
    ;; Connect the application:activate function/method with the activate
    ;; function defined above.
    (connect app application:activate activate)
    (exit
     ;; Call application:run. If there are no arguments in the command line
     ;; args, this will send the `activate' signal.
     (run app (command-line)))))


(main)
